package com.tang.common.filter;

import cn.hutool.core.util.StrUtil;
import com.tang.common.constant.cahce.BusinessCacheConstant;
import com.tang.common.constant.ParamName;
import com.tang.common.constant.SafetyConstant;
import com.tang.common.properties.TaoSystemProperties;
import com.tang.common.utils.JwtTokenUtil;
import com.tang.common.utils.cache.ParamCache;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * jwt过滤器
 * @author tang
 * @date 2021/10/24 13:21
 */
@Slf4j
public class JwtTokenFilter extends OncePerRequestFilter {

    @Resource
    private UserDetailsService userDetailsService;

    @Lazy
    @Resource
    private TaoSystemProperties taoSystemProperties;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获得令牌请求头
        String header = request.getHeader(SafetyConstant.TOKEN_NAME);
        //登录接口不需要进行验证
        if ("/login".equals(request.getRequestURI())){
            filterChain.doFilter(request,response);
            return;
        }
        if (StrUtil.isNotBlank(header) && header.startsWith(SafetyConstant.TOKEN_HEAD) && StrUtil.isNotBlank(header.substring(SafetyConstant.TOKEN_HEAD.length()))){
            //获取真实的token
            String token = header.substring(SafetyConstant.TOKEN_HEAD.length() + 1);
            //获取用户名
            String userName  = JwtTokenUtil.parseJwtUserName(SafetyConstant.SALT,token);
            //无法获取用户名,就证明jwt解析异常,token过期或者token内部数据被篡改
            if (StringUtils.isEmpty(userName)){
                log.error("token解析错误,token过期或token内部数据被篡改,本次请求不添加登录信息");
                filterChain.doFilter(request,response);
                return;
            }
            //token是否存储在redis中?
            if (taoSystemProperties.isRedisToken()){
                String redisToken = stringRedisTemplate.opsForValue().get(BusinessCacheConstant.TOKEN_PREFIX + userName);
                if (StringUtils.isEmpty(redisToken) ||!redisToken.equals(token)){
                    filterChain.doFilter(request,response);
                    return;
                }
            }
            //令牌是否需要刷新?
            if (JwtTokenUtil.refresh(SafetyConstant.SALT,token)){
                //需要刷新
                //获得令牌有效时长
                String param = ParamCache.getValue(ParamName.TOKEN_TIME);
                //生成token
                String jwt = JwtTokenUtil.createJWT(SafetyConstant.SALT, Long.valueOf(param) * 60 * 1000, userName);
                response.addHeader("refresh_token",jwt);
            }
            //登录
            UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
            //在上下文环境中设置该用户的信息和权限
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new
                    UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
            usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        }
        filterChain.doFilter(request,response);
    }

}
